package world.snowcrystal.controller;

import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import world.snowcrystal.aa.RememberMeAuthenticationSuccessHandler;
import world.snowcrystal.component.NameGenerator;
import world.snowcrystal.component.PasswordGenerator;
import world.snowcrystal.dao.UserAuthenticationDetails;
import world.snowcrystal.dao.UserDisplayDetails;
import world.snowcrystal.dto.ApplicationMessage;
import world.snowcrystal.dto.RegistrationRequestMessage;
import world.snowcrystal.dto.UsernameAndPasswordDto;
import world.snowcrystal.exception.*;
import world.snowcrystal.properties.AuthenticationProperties;
import world.snowcrystal.service.MailService;
import world.snowcrystal.service.UserAuthenticationService;
import world.snowcrystal.service.UserService;
import world.snowcrystal.utils.EmailUtils;
import world.snowcrystal.utils.RequestUtils;

import java.sql.SQLException;


@Log4j2
@Controller
@RequestMapping(value = "/user",
        produces = MediaType.APPLICATION_JSON_VALUE)
public class UserController {

    private final AuthenticationProperties authenticationProperties;
    private UserService userService;
    private MailService mailService;
    private UserAuthenticationService userAuthenticationService;

    private NameGenerator nameGenerator;
    private PasswordGenerator passwordGenerator;


    private RememberMeAuthenticationSuccessHandler cookieHandler;

    @Autowired
    public UserController(UserService userService,
                          MailService mailService,
                          UserAuthenticationService userAuthenticationService,
                          NameGenerator nameGenerator,
                          PasswordGenerator passwordGenerator,
                          RememberMeAuthenticationSuccessHandler cookieHandler, AuthenticationProperties authenticationProperties) {
        this.userService = userService;
        this.mailService = mailService;
        this.userAuthenticationService = userAuthenticationService;
        this.nameGenerator = nameGenerator;
        this.cookieHandler = cookieHandler;
        this.passwordGenerator = passwordGenerator;
        this.authenticationProperties = authenticationProperties;
    }


    @GetMapping("/this")
    public ResponseEntity<ApplicationMessage<UserDisplayDetails>> login(
            @RequestAttribute(name = "username") String username) {
        log.info("user {} request display information", username);
        UserDisplayDetails userDisplayDetails = userService.retrieveUserDisplayInfo(username);
        return ResponseEntity.ok(ApplicationMessage.ok("ok", userDisplayDetails));
    }


    @PostMapping(value = "/registration/{mail}")
    public ResponseEntity<ApplicationMessage<?>>
    sendMail(@PathVariable("mail") String mail,
             HttpServletRequest request) {
        log.info("received registration request from {} with email {}", retrieveRemoteIpv4Address(request), mail);
        //TODO 检查邮箱是否合法
        if (!EmailUtils.validThisEmail(mail)) {
            return ResponseEntity.ok(ApplicationMessage.badRequest("Invalid mail address"));
        }

        //TODO 检查这个邮箱是否被注册了
        if (!userService.checkIfMailAbsent(mail)) {
            return ResponseEntity.ok(ApplicationMessage.alreadyExists("邮箱已被注册了哦"));
        }
        //TODO 发送邮件到用户邮箱，并将回执码保存到队列中
        String vc = userAuthenticationService.generateVerificationCode();
        RegistrationRequestMessage rrm = new RegistrationRequestMessage(mail, vc);
        mailService.sendMail(mail, vc);
        userAuthenticationService.putRegistrationRequest(rrm);

        //TODO 发送成功响应
        return ResponseEntity.ok(ApplicationMessage.ok("ok", vc));

    }

    @PostMapping(value = "/registration/verification/{mail}/{verificationCode}")
    public ResponseEntity<ApplicationMessage<?>>
    verifyMail(
            @PathVariable("mail") String mail,
            @PathVariable("verificationCode") String vc,
            @RequestParam("fingerprint") String fp,
            HttpServletRequest request, HttpServletResponse response) throws SQLException {

        //TODO 验证这个验证码是否可以通过检查
        try {
            userAuthenticationService.checkValidationCode(mail, vc);
        } catch (SnowcrystalValidationCodeMismatchException e) {
            return ResponseEntity.ok(ApplicationMessage.badCredentials("验证码不匹配哦"));
        } catch (SnowcrystalValidationCodeExpireException e) {
            return ResponseEntity.ok(ApplicationMessage.notFound("认证码不存在或者已经超时了"));
        }
        //TODO 创建用户,以邮箱作为用户的默认名称
        // 使用随机生成的密码
        String password = passwordGenerator.generatePassword();
        String username = nameGenerator.generateName();
        String ip = retrieveRemoteIpv4Address(request);

        UserAuthenticationDetails user = UserAuthenticationDetails.createNewRegisteredUser(username,
                password, mail, ip);
        userAuthenticationService.addNewUserKG(user);
        log.info("新用户 {} 成功注册了,uid {}", username, user.getId());
        mailService.sendMailWithDefaultPassword(mail, password);
        //TODO 设置 Remember-me Cookie
        cookieHandler.addRememberMeCookie(username, password, response);
        cookieHandler.addFingerprintCookie(fp,response);
        //TODO 设置 in-place-operation-token
        String token = userAuthenticationService.setInPlaceOperationToken(request, response);

        //TODO 进行成功响应,顺便将生成的用户名和密码也发出去
        return ResponseEntity.ok(ApplicationMessage.ok("ok", UsernameAndPasswordDto.fromNewCreatedUserDetails(user, token)));
    }


    /**
     * @param dto
     * @see RememberMeAuthenticationSuccessHandler
     */
    @PostMapping(value = "/modification/in-place/username-password/",consumes = {"application/json"})
    public ResponseEntity<ApplicationMessage<?>>
    modifyPasswordInPlace(@RequestBody UsernameAndPasswordDto dto,
                          HttpServletRequest request, HttpServletResponse response) {
        String inPlaceOperationToken = retrieveInPlaceOperationToken(request);
        if (inPlaceOperationToken == null
                || !userAuthenticationService.checkInPlaceOperationTokenAlive(inPlaceOperationToken)) {
            return ResponseEntity.ok(ApplicationMessage.operationFailed("ipot token not exist"));
        }
        UserAuthenticationDetails uad = retrieveAuthenticatedUser(request);
        Long requestUserId = uad.getId();
        Long targetUserId = dto.getId();
        if (targetUserId == null || !targetUserId.equals(requestUserId)) {
            return ResponseEntity.ok(ApplicationMessage.badRequest("只能修改自己的信息哦"));
        }
        String newPassword = dto.getPassword();
        String newUsername = dto.getUsername();
        boolean isPasswordProvided, isUsernameProvided;
        try {
            if (isPasswordProvided = StringUtils.hasText(newPassword)) {
                userAuthenticationService.updateUserPassword(dto.getId(), null, newPassword);
            }
            if (isUsernameProvided = StringUtils.hasText(newUsername)) {
                userService.updateUsername(dto.getId(), newUsername);
            }
            cookieHandler.addRememberMeCookie(isUsernameProvided ? newUsername : uad.getUsername(),
                    isPasswordProvided ? newPassword : uad.getPassword(), response);
            return ResponseEntity.ok(ApplicationMessage.ok("ok"));
        } catch (SnowcrystalUserNotFoundException e) {
            return ResponseEntity.ok(ApplicationMessage.notFound("user not exist"));
        } catch (SnowcrystalOperationFailedException e) {
            throw new RuntimeException(e);
        } catch (SnowcrystalAlreadyExistException e) {
            return ResponseEntity.ok(ApplicationMessage.alreadyExists("用户名已经存在了哦"));
        }
    }


    /**
     * @see world.snowcrystal.aa.SUsernamePasswordAuthenticationFilter#attemptAuthentication(HttpServletRequest, HttpServletResponse)
     */
    private UserAuthenticationDetails retrieveAuthenticatedUser(HttpServletRequest request) {
        return RequestUtils.getAuthenticatedUser(request);
    }

    private String retrieveInPlaceOperationToken(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        String inPlaceOperationToken = null;
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals(authenticationProperties.getInPlaceOperationTokenCookieName())) {
                    inPlaceOperationToken = cookie.getValue();
                }
            }
        }
        return StringUtils.hasText(inPlaceOperationToken) ? inPlaceOperationToken : null;
    }


    private String retrieveRemoteIpv4Address(HttpServletRequest request) {
        String ipAddress = request.getHeader("x-forwarded-for");
        if (!StringUtils.hasText(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
            if (!StringUtils.hasText(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
                if (!StringUtils.hasText(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
                    ipAddress = request.getRemoteAddr();
                }
            }
        }
        // for test
        return StringUtils.hasText(ipAddress) ? ipAddress : null;
    }


}
